package com.lp.rpc.client;

import com.lp.rpc.client.builder.MessageBuilder;
import com.lp.rpc.codec.LpKryoDecoder;
import com.lp.rpc.codec.LpKryoEncoder;
import com.lp.rpc.constants.LpConstant;
import com.lp.rpc.domain.LpMessage;
import com.lp.rpc.registry.LocalServiceCache;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class LpClientProxy{

    private static String remoteAddress;

    private static int remotePort;

    public static void lpEnvInitializer(String remoteAddress,int remotePort){
        LpClientProxy.remoteAddress = remoteAddress;
        LpClientProxy.remotePort = remotePort;
    }

    private static Logger logger = LoggerFactory.getLogger(LpClientProxy.class);

    public static  <T> T createProxy(final Class<?> clazz,String group , String version){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 服务发现
                 */
                int index = clazz.getName().lastIndexOf(".") + 1;
                String interfaceName = clazz.getName().substring(index);
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(interfaceName);
                if (group != null && !group.equals("") && !group.equals("null")){
                    stringBuilder.append(":").append(group);
                }
                if (version != null && !version.equals("") && !version.equals("null")){
                    stringBuilder.append(":").append(version);
                }
                Map<String, Object> serviceMap = LocalServiceCache.getService(stringBuilder.toString());
                if (serviceMap.size() < 1){
                    throw new RuntimeException("服务不存在");
                }

                String host = (String) serviceMap.get(LpConstant.HOST);
                int port = (int) serviceMap.get(LpConstant.PORT);

                //服务名
                String service = (String)serviceMap.get(LpConstant.SERVICE);

                LpMessage message = MessageBuilder.build(service,method,args);

                NioEventLoopGroup group = new NioEventLoopGroup();
                LpClientHandler lpClientHandler = new LpClientHandler();
                try {
                    Bootstrap bootstrap = new Bootstrap();
                    bootstrap.group(group)
                            .option(ChannelOption.SO_KEEPALIVE,true)
                            .handler(new LoggingHandler(LogLevel.INFO))
                            .channel(NioSocketChannel.class)
                            .handler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel socketChannel) throws Exception {
                                    ChannelPipeline pipeline = socketChannel.pipeline();
                                    pipeline.addLast(new IdleStateHandler(5,5,0, TimeUnit.SECONDS))
                                            .addLast("encoder",new LpKryoEncoder())
                                            .addLast("decoder",new LpKryoDecoder())
                                            .addLast(new HeartbeatHandler())
                                            .addLast("handler", lpClientHandler);
                                }
                            });
                    ChannelFuture future = bootstrap.connect(host, port).sync().addListener(future1 -> {
                        if (future1.isSuccess()){
                            logger.info("connection successful");
                        }else {
                            logger.info("connection fail");
                        }
                    });
                    future.channel().writeAndFlush(message).sync();
                    future.channel().closeFuture().sync();
                }finally {
                    group.shutdownGracefully();
                }
                return lpClientHandler.getResponse();
            }
        });
    }
}
